#ifndef __TIMEWHEEL_HPP__
#define __TIMEWHEEL_HPP__

#include <functional>
#include <cstdint>
#include <memory>
#include <sys/timerfd.h>

// class Channel;
// class EventLoop;

using TaskFunc = std::function<void()>;    // 任务回调函数的函数类型
using ReleaseFunc = std::function<void()>; // 释放任务函数的类型
/* 设计一个定时任务类 */
class TimerTask
{
public:
    TimerTask(const uint64_t id, const uint32_t timeout, TaskFunc task_cb)
        : _id(id), _timeout(timeout), _canceled(false), _task_cb(task_cb)
    {
    }
    /* 设置释放定时任务回调——取消对shared_ptr任务对象的监控 的回调 */
    void SetRelease(const ReleaseFunc &releass_cb)
    {
        _release_cb = releass_cb;
    }
    /* 取消定时任务 */
    void Cancel()
    {
        _canceled = true;
    }
    /* 获取设置的超时时间 */
    uint32_t TimeOut() const
    {
        return _timeout;
    }
    ~TimerTask()
    {
        /* 析构函数，执行定时任务 */
        if (_canceled == false)
            _task_cb();
        _release_cb(); // 释放定时任务，移除任务id和只能指针任务对象的关联
    }

private:
    uint64_t _id;            // 定时任务对象的标识符 考虑到定时任务可能会很多，这里使用uint64_t类型\
        为了保持id的唯一性，这里由外界传入，id的唯一性由外界负责
    uint32_t _timeout;       // 定时任务超时时间
    bool _canceled;          // 定时任务是否被取消，默认为false：没被取消
    TaskFunc _task_cb;       // 定时器对象要执行的任务回调
    ReleaseFunc _release_cb; // 释放定时任务对象的回调，删除时间轮中保存的定时器对象信息
};

/* 设计一个定时轮类 */
class TimerWheel
{
    using WeakTask = std::weak_ptr<TimerTask>;
    using PtrTask = std::shared_ptr<TimerTask>; // 时间轮中的任务是智能指针任务对象
private:
    /* 释放定时任务——取消对shared_ptr任务对象的监控 */
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it != _timers.end())
            _timers.erase(it);
    }
    /* 创建定时器 */
    static int CreateTimerfd()
    {
        // 1. 创建定时器
        int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // 阻塞式
        if (timerfd < 0)
        {
            ERR_LOG("timerfd create fail");
            exit(ERROR_TIMERFD_CREATE);
        }
        // 2. 设置超时时间，启动定时器
        struct itimerspec timerspec;
        // 设置第一次超时时间
        timerspec.it_value.tv_sec = 1; // 1s
        timerspec.it_value.tv_nsec = 0;
        // 设置第一次超时后的超时时间
        timerspec.it_interval.tv_sec = 1;
        timerspec.it_interval.tv_nsec = 0;
        timerfd_settime(timerfd, 0, &timerspec, nullptr);

        return timerfd;
    }
    /* 读取定时器中的数据 */
    void ReadTimerfd()
    {
        uint64_t times;                      // 超时次数
        int ret = read(_timerfd, &times, 8); // timerfd是阻塞式读取，没有数据就阻塞
        if (ret < 0)
        {
            ERR_LOG("timerfd create fail");
            exit(ERROR_TIMERFD_READ);
        }
    }
    /* 启动时间轮，执行任务 */
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity; // 每秒钟走一次
        _wheel[_tick].clear();           // 清空这个位置的所有任务的智能指针对象，就会释放掉这些智能指针对象，\
            而释放的过程会调用回调函数执行相应任务
    }
    /* 时间到，读取定时器数值（大于0代表超时，没有超时，则定时器对应的描述符中无数据，会被阻塞），执行定时任务 */
    void OnTime()
    {
        ReadTimerfd();
        RunTimerTask();
    }
    /* 添加定时任务 */
    void TimerAddInLoop(uint64_t id, uint32_t timeout, const TaskFunc &task_cb)
    {
        PtrTask pt(new TimerTask(id, timeout, task_cb)); // 添加的是定时任务的智能指针对象
        pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        int pos = (_tick + timeout) % _capacity; // 要添加到的位置
        _wheel[pos].push_back(pt);               // 添加任务到时间轮中
        WeakTask wt(pt);                         // 用shared_ptr对象构建weak_ptr对象
        _timers[id] = wt;                        // 维护任务id和任务对象信息
    }
    /* 刷新定时任务 */
    void TimerRefreshInLoop(uint64_t id)
    {
        // 获取要刷新的定时任务的智能指针对象
        auto it = _timers.find(id);
        if (it == _timers.end())
            return;

        // 构建新任务
        PtrTask pt(it->second.lock()); // lock() -> 得到weak_ptr监控的shared_ptr对象
        // 将新任务添加到新位置
        int pos = (_tick + pt->TimeOut()) % _capacity;
        _wheel[pos].push_back(pt);
    }
    /* 取消定时任务 */
    void TimerCancelInLoop(uint64_t id)
    {
        // 获取要取消的定时任务的智能指针对象
        auto it = _timers.find(id);
        if (it == _timers.end())
            return;

        // 构建新任务
        PtrTask pt(it->second.lock()); // lock() -> 得到weak_ptr监控的shared_ptr对象
        if (pt)
            pt->Cancel();
    }

public:
    TimerWheel(EventLoop *loop) : _tick(0), _capacity(60), _wheel(_capacity), _loop(loop),
                                  _timerfd(CreateTimerfd()), _timer_channel(new Channel(_timerfd, _loop))
    {
        _timer_channel->SetReadCallback(std::bind(&TimerWheel::OnTime, this));
        _timer_channel->EnableRead();
    }

    /* 添加定时任务（EventLoop线程中进行） */
    void TimerAdd(uint64_t id, uint32_t timeout, const TaskFunc &task_cb);
    /* 刷新定时任务（EventLoop线程中进行） */
    void TimerRefresh(uint64_t id);
    /* 取消定时任务（EventLoop线程中进行） */
    void TimerCancel(uint64_t id);
    /* 是否存在某个定时任务（存在线程安全问题，必须保证此接口只在服务器内部中的EventLoop线程中被使用，不能在服务器外部使用） */
    bool HasTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
            return false;
        return true;
    }
    ~TimerWheel()
    {
    }

private:
    int _capacity;                                  // 时间轮大小，表盘大小，也是最大超时时间
    std::vector<std::vector<PtrTask>> _wheel;       // 时间轮，是一个二维数组
    int _tick;                                      // 时间指针 走到哪个位置，就代表这个位置的任务超时
    std::unordered_map<uint64_t, WeakTask> _timers; // 这里只是想能够通过任务id来得到相应的智能指针对象，不希望增加资源计数\
        所以使用weak_ptr来监视shared_ptr\
        对timers的操作，可能会在多个线程中都有，所以要考虑线程安全问题：\
            1. 加锁(代价较大)\
            2. 把相关操作都放到一个线程中，即EventLoop对应的线程中

    EventLoop *_loop;
    int _timerfd; // 定时器描述符，用来实现“时间指针”移动
    std::unique_ptr<Channel> _timer_channel;
};

#endif